﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Cosmos.Hardware;
using Cosmos.Debug;
using System.Diagnostics;
using System.Drawing.Drawing2D;
using System.Collections;
using System.Drawing.Imaging;

namespace Previewer
{
    public unsafe partial class Form1 : Form
    {
        Graphics vgaGfx;

        Bitmap vgaBuffer;

        Dictionary<Point, Color> bufferPixels = new Dictionary<Point, Color>();

        delegate void SetPixelAsync(object graphicsObject, int x, int y, Color c);

        int updatesThisSecond = 0;
        int pictureFPS = 0;

        string TitleTextFormat = "Previewer - {0} FPS | Screen - {1} FPS";

        public Form1()
        {
            InitializeComponent();

            this.TextChanged += new EventHandler(Form1_TextChanged);

            VGAScreen.VGACall += new VGAScreen.VGAEventArguments(OnVGACall);
            Cosmos.Debug.Debugger.OnDebug += new Cosmos.Debug.Debugger.DebuggerEvent(OnDebug);

            pictureBox1.Paint += new PaintEventHandler(pictureBox1_Paint);

            foreach (Control c in this.Controls)
            {
                c.Paint += new PaintEventHandler(ControlPaint);
            }
        }

        void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            pictureFPS++;
        }

        void Form1_TextChanged(object sender, EventArgs e)
        {
            label1.Text = this.Text;
        }

        void UpdateText()
        {
            this.Text = string.Format(TitleTextFormat, updatesThisSecond, pictureFPS);
        }

        void ControlPaint(object sender, PaintEventArgs e)
        {
            updatesThisSecond++;
        }

        void DrawProgressBarText(ProgressBar progressBar, string text)
        {
            int percent = (int)(((double)progressBar.Value / (double)progressBar.Maximum) * 100);
            Graphics g = progressBar.CreateGraphics();
            g.DrawString(text, new Font("Arial", (float)8.25, FontStyle.Regular), new SolidBrush(progressBar.BackColor), new PointF(progressBar.Width / 2 - 10, progressBar.Height / 2 - 7));
        }

        void OnDebug(object debug)
        {
            debugListBox.Items.Insert(0, "DEBUG::" + debug);
            try
            {
                debugListBox.Items.RemoveAt(12);
            }
            catch { }
        }

        List<Color> colors = new List<Color>();

        public void WriteDebugBox(object data)
        {
            /*debugListBox.Items.Insert(0, "DEBUG::" + data);
            try
            {
                debugListBox.Items.RemoveAt(12);
            }
            catch { }*/
            Debug.WriteLine(data);
        }

        void OnVGACall(VGAScreen.VGAEventType type, uint[] args)
        {
            WriteDebugBox("Received VGA call with type: " + type);

            Application.DoEvents();
            switch (type)
            {
                case VGAScreen.VGAEventType.SetMode:
                    pictureBox1.Size = VGAScreen._ScreenResolution;
                    colors = VGAScreen._RegisteredColours;
                    SetSize();
                    break;
                case VGAScreen.VGAEventType.SetPalette:
                    //InvalidateVGA();
                    break;
                case VGAScreen.VGAEventType.SetPixel:
                    uint x = args[0], y = args[1], c = args[2];
                    /*SetPixelAsync spa = new SetPixelAsync(SetPixel);
                    IAsyncResult result = spa.BeginInvoke(vgaGfx, (int)x, (int)y, colors[(int)c], null, null);*/
                    SetPixel((int)x, (int)y, colors[(int)c]);
                    break;
            }

            Application.DoEvents();

            pictureBox1.Invalidate();
        }

        public void ClearVGA()
        {
            vgaGfx.Clear(colors[0]);
        }

        public void SetSize()
        {
            vgaBuffer = new Bitmap(VGAScreen._ScreenResolution.Width, VGAScreen._ScreenResolution.Height);

            pictureBox1.Image = vgaBuffer;

            vgaGfx = Graphics.FromImage(vgaBuffer);

            ClearVGA();
        }

        public void SetPixel(int x, int y, Color c)
        {
            /*if (bufferPixels.ContainsKey(new Point(x, y))) bufferPixels[new Point(x, y)] = c;
            else bufferPixels.Add(new Point(x, y), c);*/
            SetPixel(pictureBox1.Image, x, y, c);
        }

        public void SetPixel(object graphicsObject, int x, int y, Color c)
        {
            Set:
            try
            {
                if (graphicsObject is Bitmap)
                {
                    Bitmap tmp = (Bitmap)graphicsObject;
                    BitmapData bd = tmp.LockBits(new Rectangle(x - 1, y - 1, 3, 3), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
                    int stride = bd.Stride;
                    IntPtr s0 = bd.Scan0;
                    byte* pixel = (byte*)s0.ToPointer();
                    int off = stride - tmp.Width * 3, wid = tmp.Width * 3;
                    for (int tx = 0; x < wid; x++)
                    {
                        for (int ty = 0; y < tmp.Height; y++)
                        {
                            pixel[0] = c.R;
                            pixel[1] = c.G;
                            pixel[2] = c.B;
                            pixel += off;
                        }
                    }
                    tmp.UnlockBits(bd);
                }
            }
            catch { goto Set; }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            VGATest();
        }

        void VGATest()
        {
            Cosmos.Debug.Debugger.Send("Loading VGA");
            VGAScreen.SetMode320x240x4();
            Cosmos.Debug.Debugger.Send("Loaded 320x240x4");
            Cosmos.Debug.Debugger.Send("Loading colors");
            VGAScreen.SetPaletteEntry(0, 0, 0, 0);
            VGAScreen.SetPaletteEntry(1, 63, 63, 63);
            VGAScreen.SetPaletteEntry(2, 127, 127, 127);
            VGAScreen.SetPaletteEntry(3, 255, 255, 255);
            VGAScreen.SetPaletteEntry(4, 191, 191, 191);
            VGAScreen.SetPaletteEntry(5, 255, 0, 0);
            VGAScreen.SetPaletteEntry(6, 0, 255, 0);
            VGAScreen.SetPaletteEntry(7, 0, 0, 255);
            Cosmos.Debug.Debugger.Send("Loaded " + VGAScreen.Colors + " colors");
            Cosmos.Debug.Debugger.Send("Loading Draw");
            uint startX = 0, startY = 0, offsetX = 0, offsetY = 0, width = 160, height = 80;

            Random r = new Random();

            uint color = 0;

            Cosmos.Debug.Debugger.Send("Drawing...");
            while(offsetX <  width)
            {
                uint x = offsetX + startX;
                offsetY = 0;

                Cosmos.Debug.Debugger.Send("X: " + (startX + offsetX) + ", drawing line");

                if(offsetX % 10 == 0) color = (uint)r.Next(1, VGAScreen.Colors - 1);

                while(offsetY < height)
                {
                    uint y = offsetY + startY;
                    VGAScreen.SetPixel(x, y, color);
                    offsetY++;
                }
                offsetX++;
            }
            Cosmos.Debug.Debugger.Send("Done drawing");
        }

        void SuperTest()
        {
            VGAScreen.SetMode320x200x8();

            if (VGAScreen.Colors == 256)
            {
                for (byte i = 0; i < 64; i++)
                {
                    VGAScreen.SetPaletteEntry(i, i, 0, 0);
                    VGAScreen.SetPaletteEntry(i + 64, 63, i, 0);
                    VGAScreen.SetPaletteEntry(i + 128, 63, 63, i);
                    VGAScreen.SetPaletteEntry(i + 192, (byte)(63 - i), (byte)(63 - i), (byte)(63 - i));

                }
            }
            else
            {
                for (byte i = 0; i < VGAScreen.Colors; i++)
                {
                    byte ii = (byte)((int)i * 64 / VGAScreen.Colors);
                    VGAScreen.SetPaletteEntry(i, ii, ii, ii);
                }
            }

            int[] xs = { 0 };
            int[] ys = { 0 };

            for (uint y = 0; y < VGAScreen.PixelHeight; y++)
            {
                for (uint x = 0; x < VGAScreen.PixelWidth; x++)
                {
                    byte m = (byte)(x + y);

                    VGAScreen.SetPixel(x, y, m);

                    //*(byte*)(0xa0000 + y * 320 + x) = m;
                }
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            SuperTest();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            Application.ExitThread();
        }

        private void SecondCalculator_Tick(object sender, EventArgs e)
        {
            UpdateText();
            updatesThisSecond = 0;
            pictureFPS = 0;
        }

        private void button4_Click(object sender, EventArgs e)
        {
            MainPreview.Init();
        }

        private void vgaRefreshDelayer_Tick(object sender, EventArgs e)
        {
            /*if (vgaBuffer != null)
            {
                List<Point> tempPoints = new List<Point>();

                for (int i = 0; i < bufferPixels.Keys.Count; i++)
                {
                    try
                    {
                        Point p = bufferPixels.Keys.ElementAt(i);
                        Color c = bufferPixels[p];
                        tempPoints.Add(p);

                        if (bufferPixels.Keys.Count - 1 == i)
                        {
                            vgaGfx.DrawPath(new Pen(new SolidBrush(c)), new GraphicsPath(tempPoints.ToArray()));
                            tempPoints.Clear();
                        }
                        else
                        {
                            if (bufferPixels.Values.ElementAt(i + 1) != c)
                            {
                                vgaGfx.DrawPath(new Pen(new SolidBrush(c)), tempPoints.ToArray());
                                tempPoints.Clear();
                            }
                        }
                    }
                    catch { }
                }
            }*/
        }
    }
}
